// Package lex 实现了词法分析器
//
// 对于大多数使用者， [Lex] 是唯一一个使用的函数
package lex

import (
	"bufio"
	"strings"

	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/utils"
)

// Lex 进行词法分析
//   - file是文件名
//   - value是已经分好行的待识别代码,必须已经去除换行符
//   - errctx是错误处理上下文
//   - EnableSinLineComments 是否启用多行注释,除非为了格式化文件，否则应该为false
func Lex(file string, value []string, errctx *errcode.ErrCtx, EnableSinLineComments bool) *FileToken {
	vlen := len(value)
	if vlen == 0 {
		return nil
	}
	FT := NewFileToken(file, vlen)
	ismlc := false
	left := 0
	scan := NewScan(value[0], ScanConfig{Errctx: errctx, Line: 0, File: file, IsMLC: &ismlc, EnableSinLineComments: EnableSinLineComments})
	for i := 0; i < vlen; i++ { //实际识别逻辑
		if ismlc { //如果之前是多行注释的内容
			if scanMLCEnd(value[i]) { //如果找到多行注释结束
				ismlc = false
				FT.LineAdd(i, NewLine(i+1, []Token{Token{TYPE: MLCEnd, Value: "*/"}}))
			} else { //如果没找到多行注释结束
				FT.LineAdd(i, NewLine(i+1, []Token{Token{TYPE: MLCIn, Value: value[i]}}))
				continue
			}
		}
		scan.Reset(value[i], i+1)
		FT.LineAdd(i, NewLine(i+1, scan.AllScan()))
		if ismlc {
			left = i
		}
	}
	if ismlc { //如果多行注释没有结束
		errctx.Panic(file, left+1, nil, errcode.MLCNoEnd)
	}
	return FT
}

// Lex2 进行词法分析
//   - file是文件名
//   - value是待分析代码
//   - errctx是错误处理上下文
//   - EnableSinLineComments 是否启用多行注释,除非为了格式化文件，否则应该为false
func Lex2(file string, value *bufio.Reader, errctx *errcode.ErrCtx, EnableSinLineComments bool) *FileToken {
	FT := new(FileToken)
	FT.File = file
	FT.Value = make([]Line, 0, 15)
	ismlc := false
	left := 0
	scan := NewScan("", ScanConfig{Errctx: errctx, Line: 0, File: file, IsMLC: &ismlc, EnableSinLineComments: EnableSinLineComments})
	i := 0
	for {
		s, end, err := utils.ReadLine(value)
		if end {
			break
		}
		if err != nil {
			panic(err)
		}
		scan.Reset(s, i+1)
		if ismlc { //如果之前是多行注释的内容
			if scanMLCEnd(s) { //如果找到多行注释结束
				ismlc = false
				FT.LineAdd(i, NewLine(i+1, []Token{{TYPE: MLCEnd, Value: "*/"}}))
			} else { //如果没找到多行注释结束
				FT.LineAdd(i, NewLine(i+1, []Token{{TYPE: MLCIn, Value: s}}))
				continue
			}
			left = i
		}
		FT.Value = append(FT.Value, NewLine(i+1, scan.AllScan()))
		i++
	}
	if ismlc { //如果多行注释没有结束
		errctx.Panic(file, left+1, nil, errcode.MLCNoEnd)
	}
	return FT
}

// checkNumber 检查是不是数字
func checkNumber(value string) (bool, TokenType) {
	vlen := len(value)
	if vlen == 0 { //如果为空字符串
		return false, 0
	}
	decimal_point := 0
	for i := 0; i < vlen; i++ {
		if isNum(value[i]) { //是数字字符
			continue
		}
		if value[i] == '.' {
			decimal_point++
			if decimal_point != 1 { //不止一个小数点
				return false, 0
			}
			continue
		}
		//存在同时非数字和非小数点字符
		return false, 0
	}
	if decimal_point == 0 {
		return true, NUMBER //是整数
	}
	return true, FLOAT //是浮点数
}

func isNum(s byte) bool {
	return s >= '0' && s <= '9'
}

// 一个文件的词法分析结果
type FileToken struct {
	// 文件名
	File string
	// 按行数升序排列的每行词法标记
	Value []Line
}

// NewFileToken 创建一个文件的词法分析结果
//   - file是文件名
//   - Len是识别行数
//   - Thread是控制是否并发
func NewFileToken(file string, Len int) *FileToken {
	ret := new(FileToken)
	ret.File = file
	ret.Value = make([]Line, Len)
	return ret
}

func (ft *FileToken) String() string {
	var buf strings.Builder
	buf.Grow(len(ft.Value))
	buf.WriteString("{")
	buf.WriteString("\t\n")
	vlen := len(ft.Value)
	for i := 0; i < vlen; i++ {
		buf.WriteString(ft.Value[i].String())
		buf.WriteString("\n")
	}
	buf.WriteString("}")
	return buf.String()
}

// LineAdd 记录一行的词法分析结果
//   - line是行数
//   - Value是一行的词法分析结果
func (ft *FileToken) LineAdd(line int, Value Line) {
	ft.Value[line] = Value
}
